package com.sheng.project.security.webmvc;

import org.springframework.context.annotation.Profile;
import org.springframework.data.domain.AuditorAware;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.jwt.JwtClaimNames;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Optional;

/**
 * @author by ls
 * @date 2023/9/8
 */
@Component
@Profile("security")
public class SecurityAuditorAware implements AuditorAware<String> {

    private static final String ANONYMOUS = "anonymous";

    private static final String USER_NAME = "user_name";

    private static final String CLIENT_ID = "client_id";

    @Override
    public Optional<String> getCurrentAuditor() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Optional<String> anonymous = Optional.of(ANONYMOUS);
        if (authentication == null) {
            return anonymous;
        }
        if (authentication instanceof UsernamePasswordAuthenticationToken && authentication.getPrincipal() instanceof UserDetails) {
            return Optional.ofNullable((UserDetails) authentication.getPrincipal()).
                    map(p -> Optional.of(p.getUsername())).
                    orElse(anonymous);
        } else if (authentication instanceof JwtAuthenticationToken && ((JwtAuthenticationToken) authentication).getToken() != null && ((JwtAuthenticationToken) authentication).getToken().getClaims() != null) {
            Map<String, Object> claims = ((JwtAuthenticationToken) authentication).getToken().getClaims();
            return Optional.ofNullable(claims.get(JwtClaimNames.SUB))
                    .map(sub -> Optional.of(sub.toString()))
                    .orElse(Optional.ofNullable(claims.get(USER_NAME))
                            .map(userName -> Optional.of(userName.toString()))
                            .orElse(Optional.ofNullable(claims.get(CLIENT_ID))
                                    .map(clientId -> Optional.of(clientId.toString()))
                                    .orElse(anonymous)));
        } else if (authentication instanceof AbstractAuthenticationToken) {
            return Optional.ofNullable(authentication.getPrincipal())
                    .map(p -> Optional.of(String.valueOf(p)))
                    .orElse(anonymous);
        } else {
            return anonymous;
        }
    }
}
